package smat.impl

/**
 * @author Aram Altschudjian
 *
 */

import scala.collection.mutable.HashMap

import smat.intr.Matrix

object MatrixJC{
  def zeros(sizes: List[Int]): MatrixJC = {
    val matrix = new MatrixJC
    matrix.init(0, sizes)
    return matrix
  }
  
  def ones(sizes: List[Int]): MatrixJC = {
    val matrix = new MatrixJC
    matrix.init(1, sizes)
    return matrix
  }
  
  def zeros(sizes: Int*): MatrixJC = {
    val matrix = new MatrixJC
    matrix.init(0, sizes:_*)
    return matrix
  }
  
  def ones(sizes: Int*): MatrixJC = {
    val matrix = new MatrixJC
    matrix.init(1, sizes:_*)
    return matrix
  }
  
  def apply(structure: Any): MatrixJC = {
    val matrix = new MatrixJC
    matrix << structure
    return matrix
  }
  
  protected def translateContent(origin: Any): Object = {
    if(origin.isInstanceOf[List[_]]){
      val list = origin.asInstanceOf[List[Any]]
      val result = new java.util.ArrayList[Object](list.length)
      list.foreach(element => result.add(translateContent(element)))
      return result
    } else if(origin.isInstanceOf[Product]){
      val tuple = origin.asInstanceOf[Product]
      val result = new java.util.ArrayList[Object](tuple.productArity)
      (0 to tuple.productArity - 1).foreach(index => result.add(translateContent(tuple.productElement(index))))
      return result
    } else {
      return origin.asInstanceOf[Object]
    }
  }
  
  protected def translateAddress(origin: Any): Object = {
    if(origin.isInstanceOf[Int] || origin.isInstanceOf[String]){
      // Performance tweaking shortcut, this case is actually covered in the ultimate `else'.
      return origin.asInstanceOf[Object]
    } else if(origin.isInstanceOf[List[_]]){
      val list = origin.asInstanceOf[List[Any]]
      val result = new java.util.ArrayList[Object](list.length)
      list.foreach(element => result.add(translateAddress(element)))
      return result
    } else if(origin.isInstanceOf[Product]){
      val tuple = origin.asInstanceOf[Product]
      val result = new Array[Object](tuple.productArity)
      (0 to tuple.productArity - 1).foreach(i => result(i) = translateAddress(tuple.productElement(i)))
      return result
    } else if(origin.isInstanceOf[scala.runtime.BoxedArray]){
      val array = origin.asInstanceOf[scala.runtime.BoxedArray]
      val result = new Array[Object](array.length)
      (0 to array.length - 1).foreach(i => result(i) = translateAddress(array(i)))
      return result
    } else if(origin.isInstanceOf[Array[Int]]){
      val array = origin.asInstanceOf[Array[Int]]
      val result = new Array[Object](array.length)
      (0 to array.length - 1).foreach(i => result(i) = translateAddress(array(i)))
      return result
    } else if(origin.isInstanceOf[Array[Any]]){
      val array = origin.asInstanceOf[Array[Any]]
      val result = new Array[Object](array.length)
      (0 to array.length - 1).foreach(i => result(i) = translateAddress(array(i)))
      return result
    } else{
      return origin.asInstanceOf[Object]
    }
  }
}

class MatrixJC extends Matrix{
  var matrix: jmat.intr.Matrix = new jmat.impl.MatrixJC()
  
  def this(matrix: jmat.intr.Matrix) = {
    this()
    this.matrix = matrix
  }
  
  def this(sizes: Int*) = {
    this()
    matrix = new jmat.impl.MatrixJC(translateArray[Int, java.lang.Integer](sizes.toArray):_*)
  }
  
  def init(defaultValue: Any, sizes: List[Int]) = {
    matrix.init(defaultValue, arrayListFromList[Int, java.lang.Integer](sizes))
  }
  
  def init(defaultValue: Any, sizes: Int*) = matrix.init(defaultValue, translateArray[Int, java.lang.Integer](sizes.toArray):_*)
  
  def data(matrix: Any) = {
    this.matrix.data(matrix)
  }
  
  def data(): Any = matrix.data
  
  def ~(): Any = matrix.`val`
  
  def entries(): Int = matrix.entries
  
  def indices(): List[Int] = (0 to matrix.entries-1).toList
  
  def indices(dimension: Int): List[Int] = (0 to matrix.size(dimension)-1).toList
  
  def <<(structure: Any) = matrix.assign(MatrixJC.translateContent(structure))
  
  def apply(address: Int): Matrix = new MatrixJC(matrix.get(address))
  
  def apply(address: String): Matrix = new MatrixJC(matrix.get(address))
  
  def apply(address: List[Any]): Matrix = new MatrixJC(matrix.get(MatrixJC.translateAddress(address)))
  
  def apply(address: Any*): Matrix = new MatrixJC(matrix.get(MatrixJC.translateAddress(address)))
  
  def update(address: Any, entry: Any) = {
    matrix.put(MatrixJC.translateAddress(address), MatrixJC.translateContent(entry))
  }
  
  def find(entry: Any): List[Int] = {
    val list = new scala.collection.jcl.ArrayList(matrix.find(entry))
    return list.map(element => element.asInstanceOf[Int]).toList
  }
  
  def count(entry: Any): Int = {
    matrix.count(entry)
  }
  
  def setDim(dimensions: Int) = {
    matrix.setDim(dimensions)
  }
  
  def getDim(): Int = {
    matrix.getDim
  }
  
  def setSize(sizes: List[Int]) = matrix.setSize(arrayListFromList[Int, java.lang.Integer](sizes))
  
  def setSize(sizes: Int*) = matrix.setSize(translateArray[Int, java.lang.Integer](sizes.toArray):_*)
  
  def size(): List[Int] = {
    val list = new scala.collection.jcl.ArrayList(matrix.size)
    return list.map(element => element.asInstanceOf[Int]).toList
  }
  
  def size(dimension: Int): Int = matrix.size(dimension)
  
  def list(): List[Any] = matrix.list.toArray.toList
  
  def unique(): List[Any] = matrix.unique.toArray.toList
  
  def isEmpty(): Boolean = matrix.isEmpty
  
  def ==(candidate: Matrix): Boolean = matrix.equals(candidate.getBackend)
  
  def rows(): List[Int] = indices(0)
  
  def cols(): List[Int] = indices(1)
  
  def pages(): List[Int] = indices(2)
  
  def max(): Any = matrix.max
  
  def maxIndex(): Int = matrix.maxIndex
  
  def min(): Any = matrix.min
  
  def minIndex(): Int = matrix.minIndex
  
  def sum(): Matrix = new MatrixJC(matrix.sum)
  
  def sumAll(): Any = matrix.sumAll
  
  def +(matrix: Matrix): Matrix = new MatrixJC(this.matrix.plus(matrix.getBackend))
  
  def +(number: Number): Matrix = new MatrixJC(matrix.plus(number))
  
  def -(matrix: Matrix): Matrix = new MatrixJC(this.matrix.minus(matrix.getBackend))
  
  def -(number: Number): Matrix = new MatrixJC(matrix.minus(number))
  
  def *(matrix: Matrix): Matrix = new MatrixJC(this.matrix.mult(matrix.getBackend))
  
  def *(number: Number): Matrix = new MatrixJC(matrix.mult(number))
  
  def /(matrix: Matrix): Matrix = new MatrixJC(this.matrix.div(matrix.getBackend))
  
  def /(number: Number): Matrix = new MatrixJC(matrix.div(number))
  
  def log(): Matrix = new MatrixJC(matrix.log())
  
  def -:(minuend: Number): Matrix = new MatrixJC(matrix.subFrom(minuend))
  
  def extend(dimension: Int) = matrix.extend(dimension, 1)
  
  def extend(dimension: Int, amount: Int) = matrix.extend(dimension, amount)
  
  def delete(dimension: Int, address: Any) = matrix.delete(dimension, MatrixJC.translateAddress(address))
  
  def delete(dimension: Int, address: Any*) = matrix.delete(dimension, MatrixJC.translateAddress(address))
  
  def delete(address: List[Any]) = matrix.delete(MatrixJC.translateAddress(address))
  
  def delete(address: Any*) = matrix.delete(MatrixJC.translateAddress(address))
  
  def setOrder(dimension: Int, order: List[Int]) = matrix.setOrder(dimension, arrayListFromList[Int, java.lang.Integer](order))
  
  def setOrder(dimension: Int, order: Int*) = matrix.setOrder(dimension, translateArray[Int, java.lang.Integer](order.toArray):_*)
  
  def addDim(size: Int, index: Int) = matrix.addDim(size, index)
  
  def setName(dimension: Int, index: Int, name: String) = matrix.setName(dimension, index, name)
  
  def setNames(dimension: Int, nameToIndexMap: HashMap[String, Int]) = null
  
  def getNames(dimension: Int): HashMap[String, Int] = null
  
  def resolve(dimension: Int, address: String): Int = matrix.resolve(dimension, address)
  
  def convert(address: Int): Array[Int] = translateArray[java.lang.Integer, Int](matrix.convert(address))
  
  def convert(address: List[Any]): Int = matrix.convert(arrayListFromList[Any, Object](address))
  
  def convert(address: Any*): Int = matrix.convert(translateArray[Any, Object](address.toArray):_*)
  
  def getBackend(): jmat.intr.Matrix = matrix
  
  override def toString(): String = matrix.toString
}
